home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr47
/
wasm223.zip
/
MACRO.DOC
< prev
next >
Wrap
Text File
|
1993-05-04
|
19KB
|
470 lines
MACRO REFERENCE
---------------
Macros exist as standard text files that are compiled and run by an
application. The library files MACRO1.ASM and MACRO2.ASM provide all
the code necessary for adding a macro language to an application. The
routine MacAll allocates the memory required for compiling and running
macros. MacCom compiles macro source files to an executable program
stored in memory. MacRun executes a compiled macro.
The default keywords of the macro language do not perform any
meaningful tasks. A table of application specific keywords must be
created by the user before the macro language can be useful.
LANGUAGE DESCRIPTION
--------------------
The macro language is stack based and resembles the Forth programming
language. The macro source code consists of a sequence of tokens
separated by spaces and line boundaries. For example:
22 11 +
33 =
The tokens in this case are "22", "11", "+", "33", and "=". The
location of the line break is unimportant. These tokens could all be
placed on a single line with the same effect:
22 11 + 33 =
Each token performs some specific stack operation. Numbers are pushed
on the stack, so after the first two tokens are "executed," the
numbers 22 and 11 are pushed on the stack. A plus (+) adds the top
two numbers of the stack together, so after the plus is executed, the
22 and 11 on the stack have been replaced by a 33. The fourth token
results in 33 being pushed on the stack (on top of the 33 remaining
after adding the 22 and 11). Finally, the equal sign compares the top
two stack numbers, replacing them with -1 (FFFF hex) if they are equal
or 0 if they are not. The net effect of this expression is to add 22
and 11 and compare the result to 33. The result of the comparison, -1
in this case, is left of the stack.
Numbers are 16 bit signed integers. If no sign is provided, '+' is
assumed. Hexadecimal numbers are specified by a preceding dollar sign
($). Characters in single quotes are translated to their ASCII value.
Some example numbers are:
1 -3 +12414 $ABCD 'a'
Symbols identify locations in the macro program. Symbols are used in
branching structures and accessing data. Symbols are defined using
the colon (:) token:
: a_symbol
In this case, "a_symbol" is the defined symbol because it is the token
after the colon. Note that symbols and keywords are case-sensitive.
Creating a symbol produces no machine code. Using a symbol pushes its
address on the stack:
a_symbol JUMP
In this case, the address of "a_symbol" is pushed on the stack and the
JUMP keyword branches to this address. Symbols may be used before
they are defined (i.e., the macro compiler handles forward references
properly).
Data can be declared in two ways, strings and arrays. Strings are
created with the double quote (") token:
" this is a string"
The start of the string begins after the first delimiter after the
double quote and ends with a double quote. In this case, the letter
't' is the first character of the string and the letter 'g' is the
last character of the string. A NUL character (ASCII code 0) is
appended to the end of all strings. Strings can contain two types of
special characters, the carat (^) and the backslash (\). The carat
specifies a control character, for example:
" this is a string^M^J"
This string has a carriage return and linefeed are appended to the end
of it. The other special character, a backslash, masks special
characters so they can be imbedded in a string, for instance:
" this is a string \^ \" \\ "
| | | | |
| | | | end of string
| | | imbed backslash
| | imbed double quote
| imbed carat
start of string
Arrays or variables (single element arrays) are created using the left
and right curved bracket ({}) tokens. All tokens within the curved
brackets are stored as sequential 16 bit values. The address of a
symbol may be stored by placing the symbol between the brackets. An
example array:
{ 0 1 -6 'a' $ABCD a_symbol }
Both strings and arrays are stored as inline data and must NOT be
executed. Strings and arrays should be placed at the end of a macro
after the QUIT or branched around. Strings and arrays are usually
referred to with symbols defined at the start of the data:
: a_variable { 55 }
: an_array { 0 1 2 3 4 }
Numbers can be stored at an address with the exclamation point (!)
token or retrieved with the '@' token:
0 a_variable ! // store zero to a_variable
a_variable @ // load the value stored at a_variable
There are four special keywords for branching. JUMP unconditionally
branches to a location stored on the stack. The location on the stack
usually comes from a symbol. ?JUMP uses a location and a number on
the stack to jump only if the number is non-zero, for instance:
22 11 + 33 = // add numbers and compare to 33, flag on stack
a_symbol ?JUMP // push address on stack, jump if flag non-zero
This last sequence of tokens jumps to "a_symbol" if the result of
adding 22 and 11 is equal to 33.
The keywords CALL and ?CALL are similar to JUMP and ?JUMP except the
current location is saved before branching. The return token, a
semicolon (;), returns from a CALL or ?CALL. A routine to add 22 and
11 then compare the result to 33 could be written as:
: add_routine 22 11 + 33 = ;
this routine is called as follows:
add_routine CALL
Comments can be included with the double slash (//) token. All
characters after a double slash token are ignored until the end of the
line is reached:
// push three numbers
1 2 // push one and two
3 // now push three
Numbers and addresses are pushed and popped from the stack without any
type checking. Keywords that expect an address on the stack (like
JUMP and CALL) have no way of knowing if the value it's using is valid
address or not. An easy way to crash a macro is running something
like:
1234 JUMP // this will almost certainly crash
Elements of an array can be accessed by incrementing the array address
by two for each element to skip:
an_array 4 + @ // load the third array element: 9
: an_array { 7 8 9 }
Array elements must be incremented by two because each element, being
16 bit values, takes up two bytes.
Accessing elements of a string are more complicated. The dereference
token '@' loads two bytes, which represents two characters of a
string. For this reason, the high byte of a dereferenced string
element must be zeroed:
a_string 8 + @ // load the ninth and tenth elements
$FF AND // this zeros high byte; return 'a'
: a_string " this is a string"
A macro may be run by MacRun once it has been successfully compiled
and loaded by MacCom. Macros begin executing from the start of the
compiled code. Macros will terminate and return from MacRun whenever
a QUIT or BREAK is encountered. If the execution stops because of a
BREAK, the state of the macro is preserved and the next call to MacRun
continues execution after the BREAK. A QUIT implies the final
termination and MacRes must be called before restarting the macro.
From a compiler standpoint, the macro language is easy to process. The
assembly equivalent of the macro expression:
22 11 + 33 =
is something like:
mov ax, 22 ; 22 token, push the number
push ax ;
mov ax, 11 ; 11 token, push the number
push ax ;
pop ax ; addition (+) token, add top two numbers
pop bx ;
add ax, bx ;
push ax ;
mov ax, 33 ; 33 token, push the number
push ax ;
pop ax ; compare (=) token, compare top two numbers
pop dx ;
sub bx, bx ;
cmp ax, dx ;
jne xyz ;
dec bx ;
xyz push bx ;
LANGUAGE KEYWORDS
-----------------
This section lists all the built-in keywords to the macro language.
Many of the keywords are identical to Forth keywords (or "words" as
they're called in Forth). Other keywords look like Forth but behave
differently.
There are two distinct types of arguments for the keywords. The first
is arguments that are taken from the source file during compilation.
The keywords that use this type of arguments are :, ", {, //, ALLOC,
and INCLUDE. All the other keywords use the stack for their arguments
during execution. The syntax for stack arguments is something like:
n1 n2 n3 - n4 n5
In this case, the keyword expects three stack arguments and returns
two stack arguments. n1, n2, and n3 are the top three stack items
before the keyword has executed. n4 and n5 are the stack items
remaining when the keyword is finished. Some keywords expect no
arguments or return no arguments, in which case the left or right side
of the dash (-) will be blank. Entry arguments (left side of dash)
and exit arguments (right side of dash) are listed left to right as
they would be pushed on the stack, so the leftmost argument is always
the top stack element. For instance, the syntax for ROT is:
n1 n2 n3 - n2 n3 n1
After a ROT (rotate), the original third item down becomes the new top
item, the original top item becomes the new second item, and the
original second item becomes the new third item:
3 4 5 ROT // after this the stack is 4 5 3, 3 is new top element
The syntax for divide (/) is:
n1 n2 - n3
where n3 = n1 / n2, so:
10 5 / // this divides 10 by 5, leaving 2 on the stack
Some keywords return different sets of arguments depending on the
entry arguments. In this case the alternate argument sets are
separated by a vertical bar (|).
Define/Declare
--------------
: symbol define a symbol
" ccccc" declare string data
{ n1, n2, ... } declare array data
ALLOC n declare n bytes of data (zeros)
! n a - store n to address a
@ a - n load n from address a
Constants
---------
FALSE - 0 load false (0)
TRUE - -1 load true (NOT 0)
HERE - n load current address
Arithmetic
----------
+ n1 n2 - n3 addition, n3 = n1 + n2
- n1 n2 - n3 subtraction, n3 = n1 - n2
* n1 n2 - n3 multiplication, n3 = n1 * n2
/ n1 n2 - n3 division, n3 = n1 / n2
1+ n1 - n2 increment, n2 = n1 + 1
1- n1 - n2 decrement, n2 = n1 - 1
2+ n1 - n2 add two, n2 = n1 + 2
2- n1 - n2 subtract two, n2 = n1 - 2
2* n1 - n2 multiply times two, n2 = n1 * 2
2/ n1 - n2 divide by two, n2 = n1 / 2
MOD n1 n2 - n3 remainder, n3 = n1 MOD n2
/MOD n1 n2 - n3 n4 divide and remainder, n3 = n1 MOD n2
n4 = n1 / n2
MIN n1 n2 - n3 minimum, n3 is minimum of n1 and n2
MAX n1 n2 - n3 maximum, n3 is maximum of n1 and n2
NEGATE n1 - n2 negative, n2 = -n1
ABS n1 - n2 absolute value, n2 is abs of n1
Stack
-----
DUP n - n n duplicate top item
?DUP n - n | n n duplicate top item if non-zero
DROP n - remove top item
SWAP n1 n2 - n2 n1 swap top two items
OVER n1 n2 - n1 n2 n1 copy second item to top
PICK ... i - n copy i-th item to top
ROT n1 n2 n3 - n2 n3 n1 roll third item to top
Conditional
-----------
= n1 n2 - 0 | -1 return -1 if items equal, 0 if not
> n1 n2 - 0 | -1 return -1 if n1 > n2, 0 if not
< n1 n2 - 0 | -1 return -1 if n1 < n2, 0 if not
0= n - 0 | -1 return -1 if n = 0, 0 if not
0> n - 0 | -1 return -1 if n > 0, 0 if not
0< n - 0 | -1 return -1 if n < 0, 0 if not
AND n1 n2 - n3 logical AND, n3 = n1 AND n2
NOT n1 - n2 logical NOT, n2 = NOT n1
OR n1 n2 - n3 logical OR, n3 = n1 OR n2
Branching
---------
; return from CALL or ?CALL
CALL a - call a
?CALL f a - call a if f
JUMP a - jump to a
?JUMP f a - jump to a if f
Other
-----
// comment
BREAK break into macro
INCLUDE f include macro file f
QUIT terminate macro
LANGUAGE EXTENSIONS
-------------------
Application specific keywords must be defined before macros can do
anything useful. The symbol MacUsr must exist to define a table
containing a list of keyword entries. Each keyword entry consists of
an ASCIIZ string (the keyword) followed by the offset of the procedure
to service that keyword. The list of entries is terminated by a null
string.
The procedure to service a keyword should be FAR. Upon entry, all the
segment registers are pointing to CS (as usual). All the other
registers are undefined. The registers SI, DI, BP, DS, and ES should
be preserved. Arguments are stored and retrieved from the macro stack
with the MacSto and MacLoa routines. Retrieved addresses are 16 bit
offsets into the macro code/data segment. MacLoa returns the macro
code/data segment in DX so you can directly access macro data by
loading DX into a segment register.
The following is a simple example that adds two keywords to the macro
language and then runs a macro called TEST.MAC:
; This program compiles and runs a macro file call TEST.MAC
; IMPORTANT: compile and allocation errors are not detected. If
; there is an error, this program will crash.
; these are the library files needed
INCLUDE 'file.asm'
INCLUDE 'buffer1.asm'
INCLUDE 'buffer2.asm'
INCLUDE 'buffer4.asm'
INCLUDE 'case1.asm'
INCLUDE 'convert.asm'
INCLUDE 'memory.asm'
INCLUDE 'stack.asm'
INCLUDE 'string.asm'
; start of program
mov ax, 1000H ;
mov bx, 1000H ;-- probably enough memory
mov cx, 1000H ;
call MacAll ;allocate macro memory
mov ax, OFFSET file
call MacCom ;compile
call MacRun ;run
call MacRel ;release macro memory
mov ax, 4C00H ;terminate function
int 21H ;execute
file DB 'test.mac',0 ;name of macro file
; this adds the keywords TYPE and INPUT to the macro language
MacUsr LABEL BYTE
DB 'TYPE',0, OFFSET TypeChar
DB 'INPUT',0, OFFSET InputChar
DB 0
; this is the TYPE service routine
TypeChar PROC FAR
call MacLoa ;load character
mov dl, al
mov ah, 2 ;DOS display function
int 21H ;display character
ret
ENDP
; this is the INPUT service routine
InputChar PROC FAR
mov ah, 8 ;DOS input function
int 21H ;input a character
sub ah, ah ;zero high byte
call MacSto ;return it
ret
ENDP
The following is a short macro that uses the keywords created above.
This macro should be copied to a file called TEST.MAC to be compiled
and run by the program above:
// this macro inputs and echoes keystrokes until ESC is pressed;
// a CR is automatically followed by a LF; all control characters
// except CR and ESC are ignored
begin JUMP // jump over skip
: skip
DROP // drop the character
: begin
INPUT // input a character
DUP 27 = exit ?JUMP // jump to exit if ESC
DUP 13 = newline ?JUMP // jump to newline if CR
DUP 32 < skip ?JUMP // jump if skip if control character
TYPE // type character
begin JUMP // loop back
: newline
TYPE // type character (a CR)
10 TYPE // type a linefeed
begin JUMP // loop back
: exit
DROP QUIT // drop ESC and quit